
#include <BWidget>
#include <BHBoxLayout>
#include "member_BHBoxLayout.h"
#include "member_BWidget.h"
#include "member_BSpacer.h"
#include "member_BSpring.h"

using namespace BWE;

#define member					(*(member_BHBoxLayout*)_ptr)
#define member_allocate()		_ptr = new member_BHBoxLayout(this)
#define member_release()		delete (member_BHBoxLayout*)_ptr

BHBoxLayout::BHBoxLayout(BWidget* host) : BLayout(host)
{
	member_allocate();
}
BHBoxLayout::BHBoxLayout(const BString& name, BWidget* host) :BLayout(name, host)
{
	member_allocate();
}
BHBoxLayout::~BHBoxLayout()
{
	member_release();
}

bool BHBoxLayout::addWidget(BWidget* widget)
{
	if (!widget)
		return false;
	if (this->host() == widget)
		return false;
	if (widget_member(widget)->layout == this)
		return true;
	member.items.append(LayoutItem());
	LayoutItem& item = member.items.last();
	item.widget = widget;
	if (widget_member(widget)->layout)
		widget_member(widget)->layout->remove(widget);
	widget_member(widget)->layout = this;
	item.attach(this->host());
	layout_member(this)->dirty = true;
	return true;
}
bool BHBoxLayout::addLayout(BLayout* layout)
{
	if (!layout || layout == this)
		return false;
	if (member.parent == layout)
		return false;
	if (!member.items.append(LayoutItem()))
		return false;
	LayoutItem& item = member.items.last();
	item.layout = layout;
	item.attach(this->host());
	this->dirty();
	return true;
}
bool BHBoxLayout::addSpacer(BSpacer* spacer)
{
	if (!spacer)
		return false;

	if (spacer_member(spacer)->layout == this)
		return true;

	if (!member.items.append(LayoutItem()))
		return false;

	LayoutItem& item = member.items.last();
	item.spacer = spacer;

	if (spacer_member(spacer)->layout)
		spacer_member(spacer)->layout->remove(spacer);
	spacer_member(spacer)->layout = this;

	this->dirty();
	return true;
}
bool BHBoxLayout::addSpacer(int width)
{
	BSpacer* spacer = new BSpacer(width, 0, Policy_Fixed, Policy_Fixed);
	return addSpacer(spacer);
}
bool BHBoxLayout::addSpring(BSpring* spring)
{
	if (!spring)
		return false;

	if (spring_member(spring)->layout == this)
		return true;

	if (!member.items.append(LayoutItem()))
		return false;

	LayoutItem& item = member.items.last();
	item.spring = spring;

	if (spring_member(spring)->layout)
		spring_member(spring)->layout->remove(spring);
	spring_member(spring)->layout = this;

	this->dirty();
	return true;
}
bool BHBoxLayout::addSpring(float stretch)
{
	BSpring* spring = new BSpring(stretch);
	return addSpring(spring);
}

bool BHBoxLayout::setWidget(int pos, BWidget* widget)
{
	if (!widget)
		return false;
	if (!member.items.check(pos))
		return false;
	int index = member.items.find(widget);
	if (index != -1)
	{
		if (index != pos)
		{
			LayoutItem item = member.items[index];
			member.items.remove(index);
			member.items.insert(pos, item);
			this->dirty();
		}
		return true;
	}
	member.items[pos].detach(this->host());
	member.items[pos] = widget;
	member.items[pos].attach(this->host());
	this->dirty();
	return true;
}
bool BHBoxLayout::setLayout(int pos, BLayout* layout)
{
	if (!layout)
		return false;
	if (!member.items.check(pos))
		return false;
	int index = member.items.find(layout);
	if (index != -1)
	{
		if (index != pos)
		{
			LayoutItem item = member.items[index];
			member.items.remove(index);
			member.items.insert(pos, item);
			this->dirty();
		}
		return true;
	}
	member.items[pos].detach(this->host());
	member.items[pos] = layout;
	member.items[pos].attach(this->host());
	this->dirty();
	return true;
}
bool BHBoxLayout::setSpacer(int pos, BSpacer* spacer)
{
	if (!spacer)
		return false;
	if (!member.items.check(pos))
		return false;
	int index = member.items.find(spacer);
	if (index != -1)
	{
		if (index != pos)
		{
			LayoutItem item = member.items[index];
			member.items.remove(index);
			member.items.insert(pos, item);
			this->dirty();
		}
		return true;
	}
	member.items[pos].detach(this->host());
	member.items[pos] = spacer;

	if (spacer_member(spacer)->layout)
		spacer_member(spacer)->layout->remove(spacer);
	spacer_member(spacer)->layout = this;

	this->dirty();
	return true;
}
bool BHBoxLayout::setSpring(int pos, BSpring* spring)
{
	if (!member.items.check(pos))
		return false;
	if (!spring)
		spring = new BSpring();
	int index = member.items.find(spring);
	if (index != -1)
	{
		if (index != pos)
		{
			LayoutItem item = member.items[index];
			member.items.remove(index);
			member.items.insert(pos, item);
			this->dirty();
		}
		return true;
	}
	member.items[pos].detach(this->host());
	member.items[pos] = spring;

	if (spring_member(spring)->layout)
		spring_member(spring)->layout->remove(spring);
	spring_member(spring)->layout = this;

	this->dirty();
	return true;
}

int BHBoxLayout::count() const
{
	return member.items.size();
}
bool BHBoxLayout::remove(int pos)
{
	return member.items.remove(pos);
}

BObject* BHBoxLayout::object(int pos) const
{
	if (member.items(pos).widget)
		return member.items(pos).widget;
	if (member.items(pos).layout)
		return member.items(pos).layout;
	if (member.items(pos).spacer)
		return member.items(pos).spacer;
	if (member.items(pos).spring)
		return member.items(pos).spring;
	return 0;
}
BWidget* BHBoxLayout::widget(int pos) const
{
	return member.items(pos).widget;
}
BLayout* BHBoxLayout::layout(int pos) const
{
	return member.items(pos).layout;
}
BSpacer* BHBoxLayout::spacer(int pos) const
{
	return member.items(pos).spacer;
}
BSpring* BHBoxLayout::spring(int pos) const
{
	return member.items(pos).spring;
}

BRect BHBoxLayout::cellRect(int pos)
{
	return member.items(pos).rect;
}

bool BHBoxLayout::setAlign(int pos, Align align)
{
	if (!member.items.check(pos))
		return false;
	LayoutItem& item = member.items[pos];
	if (item.align != align)
	{
		item.align = align;
		this->dirty();
	}
	return true;
}
Align BHBoxLayout::align(int pos) const
{
	return member.items(pos).align;
}

void BHBoxLayout::setHost(BWidget* host)
{
	if (!host)
		return;
	if (this->contain(host))
		return;
	if (this->host() == host)
		return;
	if (this->host())
	{
		for (int i = 0; i < member.items.size(); i++)
		{
			LayoutItem& item = member.items[i];
			item.detach(this->host());
		}
	}
	BLayout::setHost(host);
	if (this->host())
	{
		for (int i = 0; i < member.items.size(); i++)
		{
			LayoutItem& item = member.items[i];
			item.attach(this->host());
		}
	}
	this->dirty();
}
bool BHBoxLayout::contain(const BWidget* widget) const
{
	if (!widget)
		return false;
	for (int i = 0; i < member.items.size(); i++)
	{
		LayoutItem& item = member.items[i];
		if (item == widget)
			return true;
		if (item.layout && item.layout->contain(widget))
			return true;
	}
	return false;
}
bool BHBoxLayout::contain(const BLayout* layout) const
{
	return false;
}
bool BHBoxLayout::contain(const BSpacer* spacer) const
{
	return false;
}
bool BHBoxLayout::contain(const BSpring* spring) const
{
	return false;
}

bool BHBoxLayout::remove(const BWidget* widget)
{
	if (!widget)
		return false;
	if (member.items.remove(widget))
	{
		widget_member(widget)->layout = 0;
		this->dirty();
		return true;
	}
	for (int i = 0; i < member.items.size(); i++)
	{
		LayoutItem& item = member.items[i];
		if (item.layout && item.layout->remove(widget))
		{
			this->dirty();
			return true;
		}
	}
	return false;
}
bool BHBoxLayout::remove(const BLayout* layout)
{
	if (!layout)
		return false;
	for (int i = 0; i < member.items.size(); i++)
	{
		LayoutItem& item = member.items[i];
		if (item.layout == layout)
		{
			item.layout = 0;
			this->dirty();
			return true;
		}
	}
	return false;
}
bool BHBoxLayout::remove(const BSpacer* spacer)
{
	if (!spacer)
		return false;
	for (int i = 0; i < member.items.size(); i++)
	{
		LayoutItem& item = member.items[i];
		if (item.spacer == spacer)
		{
			item.spacer = 0;
			this->dirty();
			return false;
		}
	}
	return true;
}
bool BHBoxLayout::remove(const BSpring* spring)
{
	if (!spring)
		return false;
	for (int i = 0; i < member.items.size(); i++)
	{
		LayoutItem& item = member.items[i];
		if (item.spring == spring)
		{
			item.spring = 0;
			this->dirty();
			return false;
		}
	}
	return true;
}

void BHBoxLayout::clear()
{
	if(member.items.size())
	{
		member.items.clear();
	}
}

BSizeStretch BHBoxLayout::sizeStretch() const
{
	BSizeStretch stretch = BLayout::sizeStretch();
	if (stretch == BSizeStretch() && member.items.size())
	{
		float widthStretch = 0;
		float heightStretch = 0;
		for (int i = 0; i < member.items.size(); i++)
		{
			LayoutItem& item = member.items[i];
			stretch.widthStretch() += item.sizeStretch().widthStretch();
			if (item.spring)
				continue;
			stretch.heightStretch() = bMax(heightStretch, item.sizeStretch().heightStretch());
		}
	}
	return stretch;
}
BSizePolicy BHBoxLayout::sizePolicy() const
{
	BSizePolicy policy = layout_member(this)->sizePolicy;
	if (policy == BSizePolicy())
	{
		for (int i = 0; i < member.items.size(); i++)
		{
			LayoutItem& item = member.items[i];
			BSizePolicy sizePolicy = item.sizePolicy();
			policy.widthPolicy() |= sizePolicy.widthPolicy();
			policy.heightPolicy() |= sizePolicy.heightPolicy();
		}
	}
	return policy;
}
BSize BHBoxLayout::fixedSize() const
{
	BSize fixedSize = layout_member(this)->fixedSize;
	if (fixedSize.width() == 0 || fixedSize.height() == 0)
	{
		int border = this->border();
		int spacing = this->spacing();
		int fixedWidth = fixedSize.width();
		int fixedHeight = fixedSize.height();
		for (int i = 0; i < member.items.size(); i++)
		{
			LayoutItem& item = member.items[i];
			if (item.ignored())
				continue;
			BSize fixSize = item.fixedSize();
			fixedWidth += fixSize.width() + spacing;
			fixedHeight = bMax(fixedHeight, border + border + fixSize.height());
		}
		if (fixedSize.width() == 0)
			fixedSize.width() = fixedWidth;
		if (fixedSize.height() == 0)
			fixedSize.height() = fixedHeight;
	}
	return fixedSize;
}
BSize BHBoxLayout::sizeHint() const
{
	int border = this->border();
	int spacing = this->spacing();
	int totalWidth = border + border + spacing;
	int maxHeight = border + border;
	for (int i = 0; i < member.items.size(); i++)
	{
		LayoutItem& item = member.items[i];
		if (item.ignored())
			continue;
		BSize fixedSize = item.fixedSize();
		BSize sizeHint = item.sizeHint();
		BSize minSize = item.minSize();
		BSize maxSize = item.maxSize();
		sizeHint.limit(minSize, maxSize);

		if (item.sizePolicy().widthPolicy() == Policy_Fixed)
			totalWidth += fixedSize.width() + spacing;
		else
			totalWidth += sizeHint.width() + spacing;

		if (item.sizePolicy().heightPolicy() == Policy_Fixed)
			maxHeight = bMax(maxHeight, border + border + fixedSize.height());
		else
			maxHeight = bMax(maxHeight, border + border + sizeHint.height());
	}
	BSize hsize(totalWidth - spacing, maxHeight);
	if (hsize.width() < 0)
		hsize.width() = 0;
	if (hsize.height() < 0)
		hsize.height() = 0;
	return hsize;
}
BSize BHBoxLayout::minSize() const
{
	if (member.items.empty())
	{
		BSize minSize = layout_member(this)->minSize;
		return minSize;
	}

	int border = this->border();
	int spacing = this->spacing();
	int minWidth = border + border + spacing;
	int minHeight = border + border;
	for (int i = 0; i < member.items.size(); i++)
	{
		LayoutItem& item = member.items[i];
		if (item.ignored())
			continue;
		BSize minSize = item.minSize();
		BSize fixedSize = item.fixedSize();

		if (item.sizePolicy().widthPolicy() == Policy_Fixed)
			minWidth += fixedSize.width() + spacing;
		else
			minWidth += minSize.width() + spacing;

		if (item.sizePolicy().heightPolicy() == Policy_Fixed)
			minHeight = bMax(minHeight, border + border + fixedSize.height());
		else
			minHeight = bMax(minHeight, border + border + minSize.height());
	}
	minWidth -= spacing;
	BSize minSize = layout_member(this)->minSize;
	if (minSize.width() < minWidth)
		minSize.width() = minWidth;
	if (minSize.height() < minHeight)
		minSize.height() = minHeight;
	return minSize;
}

BSize BHBoxLayout::size() const
{
	BSize size;
	for (int i = 0; i < member.items.size(); i++)
	{
		LayoutItem& item = member.items[i];
		if (item.spring)
			continue;
		BSize itemSize = item.size();
		size.width() += itemSize.width();
		size.height() = bMax(size.height(), itemSize.height());
	}
	return size;
}
bool BHBoxLayout::adjust() const
{
	if (!this->isdirty())
		return false;

	if (this->rect().invalid() || member.items.empty())
	{
		for (int i = 0; i < member.items.size(); i++)
		{
			member.items[i].adjust(0, 0, 0, 0);
		}
		this->clean();
		return false;
	}

	if (this->name() == "debug")
		int a = 0;
	const BRect& rect = this->rect();
	int x = rect.x();
	int y = rect.y();
	int w = rect.width();
	int h = rect.height();

	BSize hintSize = this->sizeHint();
	int remain = w - hintSize.width();
	remain = member.distributeWidth(remain);

	int spacing = this->spacing();
	int border = this->border();
	int posX = x + border;
	int posY = y + border;
	int height = rect.height() - border - border;
	for (int i = 0; i < member.items.size(); i++)
	{
		LayoutItem& item = member.items[i];
		int width = item.realSize.width();
		item.adjust(posX, posY, width, height);
		posX += spacing + width;
	}

	this->clean();
	return true;
}

